package scala.examples.tcpoly.parsing

trait Results { 
  type Info
  type ErrorInfo 
  
  trait Result[+T] {
    def info: Info

    def map[U](f: T => U): Result[U]
    def flatMap[U](f: T => Result[U]): Result[U]
    def flatMapWithInfo[U](f: T => Info => Result[U]): Result[U]
    def filter(f: T => Boolean): Result[T]
    def append[U >: T](alt: => Result[U]): Result[U]
    
    def explain(ei: ErrorInfo): Result[T]
  }

  case class Success[+T](result: T, info: Info)(val zero: NoSuccess) extends Result[T] {
    def map[U](f: T => U)                              = Success(f(result), info)(zero)
    def flatMap[U](f: T => Result[U])                  = f(result)   
    def flatMapWithInfo[U](f: T => Info => Result[U])  = f(result)(info)    
    def filter(f: T => Boolean): Result[T]             = if(f(result)) this else zero
    def append[U >: T](alt: => Result[U])              = this
    
    def explain(ei: ErrorInfo) =  this
  }

  trait NoSuccess extends Result[Nothing] {
    def map[U](f: Nothing => U)                        = this
    def flatMap[U](f: Nothing => Result[U])            = this
    def flatMapWithInfo[U](f: Nothing => Info => Result[U]) = this
    def filter(f: Nothing => Boolean): NoSuccess = this
    def append[U](alt: => Result[U]): Result[U]
    
    def explain(ei: ErrorInfo): NoSuccess
    def toError: Error
  }
  
  case class Failure(errMsg: ErrorInfo, info: Info) extends NoSuccess {
    def append[U](alt: => Result[U])                   = alt
    
    def explain(ei: ErrorInfo) = Failure(ei, info)
    def toError: Error = Error(errMsg, info)
  }
  
  case class Error(errMsg: ErrorInfo, info: Info) extends NoSuccess {
    def append[U](alt: => Result[U])                   = this
    
    def explain(ei: ErrorInfo) = Error(ei, info)
    def toError: Error = this
  }  
}